// Copyright 2007 Google Inc. All Rights Reserved.
// Author: agl@imperialviolet.org (Adam Langley)
//
// Copyright (C) 2007 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This code is an example of how to use the kvs70xx library.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>
#include <fcntl.h>

#include <stdint.h>

#include "kvs70xx.h"

int usage(const char *argv0)
{
	fprintf(stderr, "Usage: %s [options] filebase\n"
		"  -d <device>: open this device\n"
		"  -l : list avalible devices\n"
		"  -n <number of pages to scan>\n"
		"  -r <resolution (100 - 600 dpi)>\n"
		"  -f : scan from flatbad\n"
		"  -w <width in inches>\n"
		"  -h <height in inches>\n"
		"  -c <compression type>\n"
		"  -s : send data to stdout\n"
		"  -q <jpeg quality factor>\n"
		"  --duplex: scan front and back\n", argv0);
	return 1;
}


void report(char *comment, uint8_t * requestsense)
{
	const char *msg = kvs70xx_strerror(requestsense);
	int code = (int) scsi_error_code(requestsense);
	fprintf(stderr, "%s: %x\n", comment, code);
	if (msg) {
		fprintf(stderr, "%s\n", msg);
	}
}

int main(int argc, char **argv)
{
	const char *device = NULL;
	float width = 8.5, height = 11.0;
	unsigned num_pages = 1;
	unsigned resolution = 300;
	unsigned flatbed = 0;
	unsigned quality = 85;
	unsigned files = 1;
	unsigned compression = 0x81;  // JPEG
	char duplex = 0;

	for (unsigned i = 1; i < argc - 1; ++i) {
		if (strcmp(argv[i], "--duplex") == 0) {
			duplex = 1;
			continue;
		}

		if (strcmp(argv[i], "-d") == 0) {
			device = argv[i + 1];
			i++;
		} else if (strcmp(argv[i], "-n") == 0) {
			num_pages = atoi(argv[++i]);
		} else if (strcmp(argv[i], "-w") == 0) {
			width = strtof(argv[++i], NULL);
		} else if (strcmp(argv[i], "-h") == 0) {
			height = strtof(argv[++i], NULL);
		} else if (strcmp(argv[i], "-q") == 0) {
			quality = strtof(argv[++i], NULL);
		} else if (strcmp(argv[i], "-r") == 0) {
			resolution = atoi(argv[++i]);
			if (resolution < 100 || resolution > 600) {
				fprintf(stderr, "Bad resolution: %s\n",
					argv[i]);
				return usage(argv[0]);
			}
		} else if (strcmp(argv[i], "-f") == 0) {
			flatbed = 1;
		} else if (strcmp(argv[i], "-c") == 0) {
			compression = 0;
		} else if (strcmp(argv[i], "-s") == 0) {
		        files = 0;
		} else if (strcmp(argv[i], "-l") == 0) {
			return kvs70xx_list_devices();
		} else {
			fprintf(stderr, "Unknown option: %s\n", argv[i]);
			return usage(argv[0]);
		}
	}

	if (argc - 1 == 0)
		return usage(argv[0]);

 	if (strcmp(argv[argc - 1 ], "-l") == 0)
		return kvs70xx_list_devices();

	const char *const filebase = argv[argc - 1];
	if (flatbed){
		num_pages = 1;
		duplex = 0;
	}

	kvs70xx_init();

	int fd = kvs70xx_open(device);

	if (fd < 0) {
		fprintf(stderr, "Cannot open scanner\n");
		return 2;
	}

	struct kvs70xx_window window;
	kvs70xx_window_init(&window);

	window.document_length = window.length = height * 1200;
	window.document_width = window.width = width * 1200;
	window.xres = window.yres = resolution;
        window.compression_type = compression;
        window.compression_argument = quality;
	window.flatbed = flatbed;

	if (num_pages > 254) {
		window.number_of_pages_to_scan = 0xff;
	} else {
		window.number_of_pages_to_scan = num_pages;
	}


	uint8_t requestsense[KVS70XX_REQUEST_SENSE_SIZE];

	if (kvs70xx_reset_windows(fd, requestsense)) {
		report("Error resetting windows", requestsense);
		return 2;
	}
	if (kvs70xx_set_windows(fd, &window, duplex, requestsense)) {
		report("Error setting windows", requestsense);
		return 2;
	}

	if (kvs70xx_scan(fd, requestsense)) {
		report("Error starting scanning", requestsense);
		return 2;
	}

	for (unsigned page = 0; page < num_pages; page++) {
		int sides = duplex ? 2:1,sd;

		char output_filename[2][128];
		int outfd[2];
		unsigned done[2] = {};
		for(int i=0; i<sides; i++) {
                  if (files) {
			snprintf(output_filename[i], sizeof(output_filename[i]), "%s-%s-%d.jpeg",
			 filebase, i ? "back" : "front", page);
			outfd[i] = open(output_filename[i],
				       O_WRONLY | O_CREAT | O_TRUNC, 0644);
			if (outfd[i] < 0) {
				fprintf(stderr,
					"Failed to write to %s: %s\n",
					output_filename[i], strerror(errno));
				return 2;
			}
                  } else {
                    outfd[i] = fileno(stdout);
                  }
		}
		if (kvs70xx_data_buffer_wait(fd, &sd, requestsense)) {
						report("Error waiting for image data",  requestsense);
						return 2;
		}

		char eof[2] = {0, !duplex};
		for (int side = 0;;) {
			uint8_t buffer[KVS70XX_BUFFER_SIZE];
			unsigned written;
			char ili;
			if (kvs70xx_read_data
			    (fd, page%256, side, buffer,
			     sizeof(buffer), &written, &eof[side], &ili,
			     requestsense)) {
				report("Error reading image",
				       requestsense);
				return 2;
			}

			write(outfd[side], buffer, written);
			done[side] += written;

			if (eof[0] && eof[1]){
				break;
			}

			if(duplex && (eof[side] || ili || !written)){
				if(eof[side] ||  !written)
					side = side ? 0:1;
			}
		}
		for(int i=0; i<sides; i++) {
			uint32_t width, height;
			if (kvs70xx_picture_size
			    (fd, page%256, i, &width, &height, requestsense)) {
				report("Error getting page size", requestsense);
				return 2;
			}
		}
		for(int i=0; i<sides; i++) {
			fprintf(stderr, "%s: %d bytes\n", output_filename[i], done[i]);
			close(outfd[i]);
		}
	}

}
